﻿/*****************************************************************
* 模 块 名 : 
* 文 件 名 : yuv.c
* 相关文件 : 
* 功    能 : 
* 作    者 : Xsky
* 版    本 : V0.1
* -------------------------------------------------------------
* 修改记录 : 
* 日  期     	版本        修改人      修改内容
* 2017年5月24日 	v0.1        Xsky        创建
*****************************************************************/

#define _CRT_SECURE_NO_WARNINGS

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>

#if __linux__
#include <unistd.h>
#endif

//#include "type_def.h"
#include "base_type.h"
//#include "strutil.h"

#define DBG_INFO_EN		0

#if _WIN32
#define sleep(s)			Sleep((s) * 1000)
#endif

#define get_now_ms(...)			0

typedef struct _TYuvVideo_
{
	char file_name[255];
	FILE	*pf;
	uint8_t *pu8Data;
	int		is_read_all;
	int		w;
	int		h;
	int		per_bytes;	// 每一帧读取的长度
	unsigned int		frame_no;
	unsigned int		frame_total;
	int		file_bytes;
}TYuvVideo;

void *yuv_video_create(const char *pfile_name, int width, int height, int is_read_all, int read_frames)
{
	TYuvVideo *pyuv = malloc(sizeof(TYuvVideo));
	if ( pyuv == NULL )
	{
		return NULL;
	}

	memset(pyuv, 0, sizeof(TYuvVideo));

	strncpy(pyuv->file_name, pfile_name, sizeof(pyuv->file_name));
	pyuv->per_bytes = width * height * 3 / 2;
	pyuv->w = width;
	pyuv->h = height;
	pyuv->frame_no = 0;
	
	pyuv->is_read_all = is_read_all;

	pyuv->pf = fopen(pfile_name, "rb");
	if ( NULL == pyuv->pf ) { 
		printf("open file:%s, error!\r\n", pfile_name);
		free(pyuv);
		return NULL;
	}

	unsigned int nFileBytes = 0;
	fseek(pyuv->pf, 0, SEEK_END);
	nFileBytes = ftell(pyuv->pf);
	fseek(pyuv->pf, 0, SEEK_SET);

	pyuv->frame_total = (int)((float)nFileBytes / (pyuv->per_bytes));

	if ( NULL != pyuv->pf )
	{

		//if ( nFileBytes > 250 * 1024 * 1024)
		//{
		//	// 大于255帧时读取会异常
		//	is_read_all = FALSE;
		//}

		if ( is_read_all )
		{
			unsigned int nR = read_frames * pyuv->per_bytes;
			if ( nR <= 0 || nR > nFileBytes )
			{
				nR = nFileBytes;
				read_frames = (int)((float)nR / (float)(pyuv->per_bytes));
			}

			pyuv->pu8Data = malloc(nR);
			if ( NULL == pyuv->pu8Data )
			{
				printf("***read all malloc err:%dx%d,%dB!\n", read_frames, pyuv->per_bytes, nR);
				pyuv->is_read_all = FALSE;
				#if __linux__
				sleep(10);
				#else
				Sleep(10000);
				#endif

			}
			else 
			{
				printf("ready read file %1.3f MB\n\r", (float)nR / (1024 * 1024));
				const unsigned int RD_BLOCK_SIZE = nR / 20;		// 1M
				unsigned int nRead = 0;
				uint8_t *pdst = pyuv->pu8Data;
				uint64_t t_start = get_now_ms();
				do 
				{
					int iTry = 0;
					do
					{
						uint64_t t_read = get_now_ms();
						unsigned int n = fread(pdst, 1, RD_BLOCK_SIZE, pyuv->pf);
						if ( n > 0 )
						{
							nRead += n;
							pdst += n;
							uint64_t t_ms = get_now_ms() - t_read;
							printf("read %8dB, %3.1f/%4.3fMB %2.2f%%; %1.3fMB/s\n", n, (float)nRead / (1024 * 1024),
									(float)nR/(1024*1024), (float)(nRead) * 100 / (float)nR, 
									n * 1000.0 / (1024*1024) / t_ms);
							break;
						}
						if ( nRead >= nR ) {
							goto lb_read_finished;
						}
						printf("\n***read error:@%dB; ", nRead);
						#if __linux__
						usleep(1000);
						#else
						Sleep(1000);
						#endif

						if ( feof(pyuv->pf) )
						{
							goto lb_read_finished;
						}
					} while (iTry++ < 3);
				} while (nRead < nR);
			lb_read_finished:
				{
					uint64_t t_total = get_now_ms() - t_start;
					printf("%1.3fs, %1.3f MB/s\r\n", t_total / 1000.0f, nRead * 1000.0f / (1024 * 1024) / t_total);
				}
				printf("\n\r");

				if ( nRead <= 0 )
				{
					free(pyuv->pu8Data);
					pyuv->pu8Data = NULL;
					pyuv->is_read_all = FALSE;
					printf("***read all file err!\n");
					#if __linux__
					sleep(10);
					#else
					Sleep(10000);
					#endif

				}
				else 
				{
					pyuv->file_bytes = nR;
					//assert( pyuv->per_bytes != 0 );
					//read_frames = nR / (int)(pyuv->per_bytes);	// 该行会导致指令错误
					pyuv->frame_total = read_frames;
					printf("read file %d of %d B, frames:%d\n", nR, nFileBytes, read_frames);
				}
			}
		}
	}


	return pyuv;
}
int yuv_video_destroy(void **pphandle)
{
	printf("yuv_video_destroy\n");
	if ( NULL == pphandle ){
		return -1;
	}
	if ( NULL == *pphandle ) {
		return -2;
	}

	TYuvVideo *pyuv = (TYuvVideo *)*pphandle;
	if ( NULL != pyuv->pf )
	{
		fclose(pyuv->pf);
		pyuv->pf = NULL;
	}

	if ( NULL != pyuv->pu8Data )
	{
		free(pyuv->pu8Data);
		pyuv->pu8Data = NULL;
	}

	free(pyuv);

	*pphandle = NULL;

	return 0;
}

unsigned int yuv_get_total_frame(void *phandle)
{
	if ( phandle == NULL ) {
		return 0;
	}
	TYuvVideo *pyuv = (TYuvVideo *)phandle;
	return pyuv->frame_total;
}

// 注意读取长度
int yuv_read_next_loop(void *phandle, uint8_t *pdata, int bufsize, unsigned int *pframe_no, int *pis_restart)
{
	*pframe_no = -1;
	*pis_restart = FALSE;

	if ( phandle == NULL ) {
		return -1;
	}
	TYuvVideo *pyuv = (TYuvVideo *)phandle;

	if ( pyuv->pf == NULL )
	{
		return -2;
	}

	*pframe_no = pyuv->frame_no;

	int nRBytes = 0;
	if ( pyuv->is_read_all && pyuv->pu8Data != NULL )
	{
		#if DBG_INFO_EN
		printf("cp yuv %d/%d %dB\n", pyuv->frame_no, pyuv->frame_total, pyuv->file_bytes);
		#endif
		
		memcpy(pdata, pyuv->pu8Data + pyuv->per_bytes * pyuv->frame_no, pyuv->per_bytes);
		nRBytes = pyuv->per_bytes;
		pyuv->frame_no++;
		if ( pyuv->frame_no >= pyuv->frame_total ) {
			pyuv->frame_no = 0;
			#if DBG_INFO_EN
			printf("---re first frame.\n");
			#endif

			*pis_restart = TRUE;
		}
	}
	else 
	{
		nRBytes = fread(pdata, 1, pyuv->per_bytes, pyuv->pf);
		if ( nRBytes > 0 )
		{

			printf("read yuv %d %dB\n", pyuv->frame_no, nRBytes);
			pyuv->frame_no ++;
		}
		else 
		{
			printf("***read yuv %d %dB err\n", pyuv->frame_no, nRBytes);
		}

		if ( feof(pyuv->pf) )
		{
			fseek(pyuv->pf, 0, SEEK_SET);
			pyuv->frame_no = 0;
			#if DBG_INFO_EN
			printf("---read first frame.\n");
			#if __linux__
			sleep(3);
			#else
			Sleep(3000);
			#endif
			#endif

			*pis_restart = TRUE;
		}
	}

	return nRBytes;
}

// 跳至相应的帧索引，从0开始
int yuv_goto_frame(void *phandle, unsigned int frame_no)
{
	if ( phandle == NULL ) {
		return -1;
	}
	TYuvVideo *pyuv = (TYuvVideo *)phandle;
	if ( frame_no >= 0 && frame_no < pyuv->frame_total ) {
		pyuv->frame_no = frame_no;

		if ( !pyuv->is_read_all ) {
			if ( NULL != pyuv->pf ) {
				long pos = frame_no * pyuv->per_bytes;
				fseek(pyuv->pf, pos, SEEK_SET);
			}
			else {
				printf("***yuv video goto frame err NULL file!\n");
				#if __linux__
				sleep(5);
				#else
				Sleep(5000);
				#endif
			}
		}

		return 0;
	}
	else {
		printf("***yuv video goto frame:%d, error out of range, max:%d!\n", frame_no, pyuv->frame_total);
		sleep(3);
	}
	return -2;
}

int read_yuv_file(void *pdata, size_t bytes, const char *pFileName)
{
	FILE *pf = NULL;
	int nRBytes = 0;
	pf = fopen(pFileName, "rb");

	printf("%s pf:%p\n", pFileName, pf);
	if ( pf != NULL )
	{
		long nFileBytes = 0;
		fseek(pf, 0, SEEK_END);
		nFileBytes = ftell(pf);
		fseek(pf, 0, SEEK_SET);

		if ( nFileBytes != bytes )
		{
			printf("file %ld B != need:%d B!\r\n", nFileBytes, bytes);
		}
		else {
			printf("file %ld B == need:%d B\r\n", nFileBytes, bytes);
		}

		nRBytes = fread(pdata, 1, bytes, pf);
	}
	else {
		printf("open flie %s err!\r\n", pFileName);
	}

	if ( pf != NULL ) {
		fclose(pf);
	}

	return nRBytes;
}

// 单色
void yuv_copy_rect(uint8_t *pdst, int dst_w, int dst_h, uint8_t *psrc, int src_w, int src_h,
	int dst_x, int dst_y, CV_RECT *psrc_rect)
{
	uint8_t *py0 = pdst;
	uint8_t *pu0 = py0 + dst_w * dst_h;
	uint8_t *pv0 = pu0 + ((dst_w * dst_h) >> 2);

	uint8_t *pysrc0 = psrc;
	uint8_t *pusrc0 = pysrc0 + src_w * src_h;
	uint8_t *pvsrc0 = pusrc0 + ((src_w * src_h) >> 2);

	int width = psrc_rect->width;
	int height = psrc_rect->height;
	if (width > dst_w) width = dst_w;
	if (height > dst_h) height = dst_h;

	width &= ~0x01;
	height &= ~0x01;
	dst_y &= ~0x01;
	dst_x &= ~0x01;


	int end_x = dst_x + width;
	int end_y = dst_y + height;
	if (end_x > dst_w) end_x = dst_w;
	if (end_y > dst_h) end_y = dst_h;

	int dstuv_step = dst_w >> 1;
	int srcuv_step = src_w >> 1;
	for (int y = dst_y; y < end_y; y += 2)
	{
		int dstuv_ofst = dstuv_step * (y >> 1) + (dst_x >> 1);
		uint8_t *py = py0 + dst_w * y + dst_x;
		uint8_t *py1 = py + dst_w;
		uint8_t *pu = pu0 + dstuv_ofst;
		uint8_t *pv = pv0 + dstuv_ofst;

		int src_y = psrc_rect->y + (y - dst_y);
		int srcuv_ofst = srcuv_step * (src_y >> 1) + (psrc_rect->x >> 1);
		uint8_t *pysrc = pysrc0 + src_w * src_y + psrc_rect->x;
		uint8_t *pysrc1 = pysrc + src_w;
		//uint8_t *pusrc = pusrc0 + srcuv_ofst;
		//uint8_t *pvsrc = pvsrc0 + srcuv_ofst;

		for (int x = dst_x; x < end_x; x += 2)
		{
			*py++ = *pysrc++;
			*py++ = *pysrc++;
			*py1++ = *pysrc1++;
			*py1++ = *pysrc1++;

			*pu++ = 0x80;	// *pusrc++;
			*pv++ = 0x80;	// *pvsrc++;
		}
	}
}

// 三色
void yuv_copy_rect_ex(uint8_t *pdst, int dst_w, int dst_h, uint8_t *psrc, int src_w, int src_h,
			int dst_x, int dst_y, CV_RECT *psrc_rect)
{
	uint8_t *py0 = pdst;
	uint8_t *pu0 = py0 + dst_w * dst_h;
	uint8_t *pv0 = pu0 + ((dst_w * dst_h) >> 2);

	uint8_t *pysrc0 = psrc;
	uint8_t *pusrc0 = pysrc0 + src_w * src_h;
	uint8_t *pvsrc0 = pusrc0 + ((src_w * src_h) >> 2);

	int width = psrc_rect->width;
	int height = psrc_rect->height;
	if (width > dst_w) width = dst_w;
	if (height > dst_h) height = dst_h;

	width &= ~0x01;
	height &= ~0x01;
	dst_y &= ~0x01;
	dst_x &= ~0x01;

	int end_x = dst_x + width;
	int end_y = dst_y + height;
	if (end_x > dst_w) end_x = dst_w;
	if (end_y > dst_h) end_y = dst_h;

	int dstuv_step = dst_w >> 1;
	int srcuv_step = src_w >> 1;
	for (int y = dst_y; y < end_y; y += 2)
	{
		int dstuv_ofst = dstuv_step * (y >> 1) + (dst_x >> 1);
		uint8_t *py = py0 + dst_w * y + dst_x;
		uint8_t *py1 = py + dst_w;
		uint8_t *pu = pu0 + dstuv_ofst;
		uint8_t *pv = pv0 + dstuv_ofst;

		int src_y = psrc_rect->y + (y - dst_y);
		int srcuv_ofst = srcuv_step * (src_y >> 1) + (psrc_rect->x >> 1);
		uint8_t *pysrc = pysrc0 + src_w * src_y + psrc_rect->x;
		uint8_t *pysrc1 = pysrc + src_w;
		uint8_t *pusrc = pusrc0 + srcuv_ofst;
		uint8_t *pvsrc = pvsrc0 + srcuv_ofst;

		for (int x = dst_x; x < end_x; x += 2)
		{
			//if (*pysrc != 0 || *pusrc != 0 || *pvsrc != 0 || *pysrc1 != 0) {
			//	printf("not 0\r\n");
			//}

			*py++ = *pysrc++;
			*py++ = *pysrc++;
			*py1++ = *pysrc1++;
			*py1++ = *pysrc1++;

			*pu++ = *pusrc++;
			*pv++ = *pvsrc++;
		}
	}
}
